/* Repository for game data relevant to multiple AI behaviors.  Examples include:
		--Current unit counts
		--Resource counts
		--Building counts/types
		--Attacking? Defending?
		--"Spy/scouting" reports of enemy buildings/units
		--Building locations
		--etc

*/

#ifndef BLACKBOARD_H_
#define BLACKBOARD_H_

#include "Logging.h"
#include "Singleton.h"
#include <set>
#include <list>
#include <BWAPI.h>
#include <assert.h>
#include "Base.h"
#include "BuildOrder.h"
#include "ThreatMap.h"
#include "MarineSM.h"

//#define DEBUG
#ifndef DEBUG
#define DEBUG_PRINT(x, ...)
#else
#define DEBUG_PRINT(x, ...)	do {	BWAPI::Broodwar->printf(x,__VA_ARGS__);		\
									log(x,__VA_ARGS__);							\
							} while(0)
#endif
			

static Order oneRaxFE[7] = {	{	9, BWAPI::UnitTypes::Terran_Supply_Depot, 1, BUILD_BUILDIDNG},
									{11, BWAPI::UnitTypes::Terran_Barracks, 1, BUILD_BUILDIDNG},
									{14, BWAPI::UnitTypes::Terran_Barracks, 1, BUILD_BUILDIDNG},
									{15, BWAPI::UnitTypes::Terran_Marine, 1, BUILD_UNIT},
									{16, BWAPI::UnitTypes::Terran_Marine, 1, BUILD_UNIT},
									{17, BWAPI::UnitTypes::Terran_Supply_Depot, 1, BUILD_BUILDIDNG},
									{18, BWAPI::UnitTypes::Terran_Marine, 2, BUILD_UNIT}
								};

struct BuildingCallback
{
	bool canScheduleToBuild;
	int frameScheduled;
	bool Expired()	{	return ((BWAPI::Broodwar->getFrameCount() - frameScheduled)>80);	}
	void Schedule()	{	frameScheduled = BWAPI::Broodwar->getFrameCount();	}
};


#define BB BlackBoard::GetInstancePtr() // Convenience for accessing BlackBoard

#define TILE_SIZE 32

BWAPI::Position TileToPosition(BWAPI::TilePosition tile);


class ThreatMap;
class BlackBoard : public Singleton<BlackBoard>
{
public:
	friend class Singleton<BlackBoard>;

	//CheckConditions
	bool isBuildProductionBuildings();
	bool isBuildResearchBuildings();
	bool isBuildAirUnits();
	bool isBuildInfantryUnits();
	bool isBuildMechUnits();
	bool isBuildUnit(BWAPI::UnitType type);
	bool isBuildBuilding(BWAPI::UnitType type);
	bool isResearch(BWAPI::UpgradeType type,int upgradeLevel);
	bool isResearch(BWAPI::TechType type);

	std::list<Base*>* GetBases()	{	return &bases;	}
	std::list<Base*>* GetEnemyBases()	{	return &enemyBases;	}
	std::map<BWAPI::UnitType, std::list<BWAPI::TilePosition>>*	GetEnemyBuildingLocations()	{	return &enemyBuildingPositions;	}

	unsigned int GetMineralsToSpend()				{	return mineralBalanceToSpend;	}
	unsigned int GetGasToSpend()					{	return gasBalanceToSpend;		}
	unsigned int GetSupplyToSpend()					{	return supplyBalanceToSpend;	}

	std::list<BWAPI::Unit*>* GetBuildingsByTypeGeneral(BWAPI::UnitType type);
	std::list<BWAPI::Unit*>* GetBuildingBuildingsByTypeGeneral(BWAPI::UnitType type);
	std::list<BWAPI::Unit*>* GetUnitsByTypeGeneral(BWAPI::UnitType type);
	int NumberOfEnemyAirUnits();
	int NumberOfEnemyGroundUnits();
	
	void AddUnit(BWAPI::Unit*);
	std::list<BWAPI::TilePosition>* GetPossibleExpansions(){	return &possibleExpansions;		}
	ThreatMap* GetThreatMap() {return threatMap;}
	std::list<MarineSM*>* GetMarineSMs() {return &marineSMs;}

	void Initialize();
	void AddEnemyUnit(BWAPI::Unit* enemy);
	void AddEnemyBase(BWAPI::Unit* enemy);
	std::set<BWAPI::Unit*>* GetEnemyUnitsByType(BWAPI::UnitType typ);
	void RemoveEnemyUnit(BWAPI::Unit* enemy);
	void RemoveUnit(BWAPI::Unit*);
	void Refund(BWAPI::UnitType);
	void ScanForRefinery();
	void SpendResources(BWAPI::UnitType type);
	void SpendGas(unsigned int gas);
	void SpendMinerals(unsigned int min);
	void SpendSupply(unsigned int supply)			{	/*realSupplyUsed += supply;*/		}
	void SetAttackPosition(BWAPI::TilePosition tile)	{	currentAttackTarget = tile;	}
	BWAPI::TilePosition& GetAttackPosition()			{	return currentAttackTarget;	}
	void ResetOrder();
	void UpdateGameState();	
	void SetCloakedEnemyPosition(BWAPI::Position pos)	{	cloakedEnemyPos = pos;	isCloakedEnemy = true;	}
	bool GetCloakedEnemyPosition(BWAPI::Position* toReturn);

//Resource counts our units will actually use.  Spent when buildings/units are QUEUED to be built/created rather than
	//when construction/training starts as the game does.
	unsigned int mineralBalanceToSpend;
	unsigned int gasBalanceToSpend;
	unsigned int mineralsLastFrame;
	unsigned int gasLastFrame;

	unsigned int supplyBalanceToSpend;
	unsigned int realSupplyUsed;

private:
	BlackBoard();
	~BlackBoard() {}
	void AddBuildingBuildingGeneral(BWAPI::Unit* building);
	void AddBuildingGeneral(BWAPI::Unit* building);
	void AddUnitGeneral(BWAPI::Unit* unit);
	bool RemoveUnitGeneral(BWAPI::Unit* unit);
	bool RemoveBuildingGeneral(BWAPI::Unit* unit);
	void UpdateBuildingBuildings();

	unsigned int randomRoll;

	int refineryFrameCount;
	bool scanForRefinery;
	std::map<BWAPI::UnitType, std::list<BWAPI::TilePosition>> enemyBuildingPositions;
	
	BWAPI::Position cloakedEnemyPos;
	bool isCloakedEnemy;

	std::map<BWAPI::UnitType, BuildingCallback> isBuild;
	
	Base* mainBase;
	std::list<Base*> bases;
	std::list<Base*> enemyBases;

	BuildOrder* build;
	Order*	currentOrder;
	ThreatMap* threatMap;

	std::map<BWAPI::UnitType, std::set<BWAPI::Unit*>> enemyUnits;
	int enemyGroundNumber;
	int enemyAirNumber;

	std::list<BWAPI::TilePosition> possibleExpansions;
	std::map<BWAPI::UnitType, std::list<BWAPI::Unit*>> buildingLookup;
	std::map<BWAPI::UnitType, std::list<BWAPI::Unit*>> buildingBuildings;
	std::map<BWAPI::UnitType, std::list<BWAPI::Unit*>> unitLookup;

	std::list<MarineSM*> marineSMs;

	BWAPI::TilePosition currentAttackTarget;
};

inline BWAPI::Position TileToPosition(BWAPI::TilePosition tile)
{
	return BWAPI::Position(tile.x()*TILE_SIZE,tile.y()*TILE_SIZE);
}

template<class T, class X>
inline T* CastToBase(X* x)
{
	return (T*)x;
}

#endif
